#include <iostream>
#include <string>
#include <vector>
#include "util.hpp"
#include <boost/filesystem.hpp>

// 存放待处理的html文件根目录路径
const std::string src_path = "data/input/";
// 存放解析后的数据保存路径
const std::string output = "data/raw_html/raw.txt";

enum ExitErr{
	SUCCESS = 0,
	ENUM_ERR,
	PARSER_ERR,
	SAVE_ERR
};

struct DocInfo{
	std::string title; // 文档标题
	std::string content; // 文档正文
	std::string url; // 文档url
};


bool EnumFile(const std::string& src_path, std::vector<std::string>& file_lists);
bool ParseHtml(const std::vector<std::string>& file_lists, std::vector<DocInfo>* result);
bool SaveHtml(const std::vector<DocInfo>& result, const std::string& output);

int main()
{
	// 获取所有待处理的html文件路径
	std::vector<std::string> file_lists;

	// 枚举html文件
	if(!EnumFile(src_path, file_lists)){
		std::cerr << "枚举html文件失败" << std::endl;
		return ENUM_ERR;
	}

	// 解析html文件
	std::vector<DocInfo> result;
	if(!ParseHtml(file_lists, &result)){
		std::cerr << "解析html文件失败" << std::endl;
		return PARSER_ERR;
	}
	
	// 保存解析结果
	if(!SaveHtml(result, output)){
		std::cerr << "保存解析结果失败" << std::endl;
		return SAVE_ERR;
	}

	return 0;
}

bool EnumFile(const std::string& src_path, std::vector<std::string>& file_lists){
	namespace fs = boost::filesystem;
	fs::path path = src_path;
	if(!fs::exists(path)){
		std::cerr << "html文件路径不存在" << std::endl;
		return false;
	}

	//遍历目标目录
	fs::recursive_directory_iterator end;
	int file_num = 0;
	for(fs::recursive_directory_iterator iter(src_path); iter != end; iter++){
		// 筛选正常文件
		if(!fs::is_regular_file(*iter))
			continue;

		// 筛选html文件
		if(iter->path().extension() != ".html")
			continue;

		std::string file = iter->path().string();
		file_lists.push_back(file); 

		// std::cout << file << std::endl;	
		// file_num++;	
	}
	// std::cout<<"共" << file_num << "个文件" << std::endl;

	return true;
}

bool ParseTitle(const std::string& text, std::string* title){ 
	if(text.empty())
		return false;

	auto begin = text.find("<title>");
	if(begin == std::string::npos)
		return false;

	auto end = text.find("</title>");
	if(end == std::string::npos)
		return false;

	begin += std::string("<title>").size();
	if(begin >= end)
		return false;

	*title = text.substr(begin, end - begin);

	return true;
}

bool ParseContent(const std::string& text, std::string* content){
	if(text.empty())
		return false;

	enum State{
		LABEL,
		CONTENT
	};

	State s = LABEL;
	for(char c : text){
		switch(s){
			case LABEL:
				if(c == '>')
					s = CONTENT;
				break;

			case CONTENT:
				if(c == '<')
					s = LABEL;
				else
					content->push_back(c);
				break;

			default:
				break;
		}
	}

	return true;
}

bool ParseUrl(const std::string& file, std::string* url){
	std::string url_head = "https://www.boost.org/doc/libs/1_89_0/doc/html/";
	std::string url_tail = file.substr(src_path.size());
	*url = url_head + url_tail;

	return true;
}

// debug print doc
void ShowDoc(const DocInfo& doc){
	std::cout << "title: " << doc.title << std::endl;
	std::cout << "url: " << doc.url << std::endl;
	std::cout << "content: " << doc.content << std::endl;
}

bool ParseHtml(const std::vector<std::string>& file_lists, std::vector<DocInfo>* result){
	for(auto file : file_lists){
		// 1. 读取文件内容
		std::string text;
		if(!ns_util::FileUtil::ReadFile(file, &text))
			continue;

		DocInfo doc;
		// 2.解析指定文件，提取title
		if(!ParseTitle(text, &doc.title)){
			continue;
		}

		// 3.解析指定文件，提取content
		if(!ParseContent(text, &doc.content)){
			continue;
		}

		// 4.解析指定文件路径，构建url
		if(!ParseUrl(file, &doc.url)){
			continue;
		}

		result->push_back(std::move(doc));

		// debug
		// ShowDoc(doc);
		// break; // 测试时只解析一个文件
	}

	return true;
}

bool SaveHtml(const std::vector<DocInfo>& result, const std::string& output){
#define SEP '\3'
	std::ofstream out(output, std::ios::out | std::ios::binary);
	if(!out.is_open()){
		std::cerr << "保存文件失败" << std::endl;
		return false;
	}

	for(auto item : result){
		std::string out_string;
		out_string = item.title;
		out_string += SEP;
		out_string += item.content;
		out_string += SEP;
		out_string += item.url;
		out_string += '\n';

		out.write(out_string.c_str(), out_string.size());
	}

	return true;
}

